import { customAlphabet } from "nanoid";
import { PrismaClient, table_info } from "@prisma/client";
import { Prisma } from ".prisma/client";

type ModelName = Prisma.ModelName;

declare namespace CreateOption {
   interface ID {
      /** 表名称 */
      tableName: ModelName;
   }

   interface IDs extends ID {
      /** 生成数量 */
      total: number;
   }

   type IDCallBack = (id: string) => Promise<void>;

   type IDsCallBack = (id: string[]) => Promise<void>;

   interface Count {
      "count(*)": number;
   }
}

const prisma = new PrismaClient();

/** 创建条目 */
export class Create {
   static #nanoid = customAlphabet("1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ", 29);

   /**
    * @function 根据前缀及序号生成id
    * @param prefix id 前缀
    * @param index 序号
    * @returns id
    */
   static #getID(prefix: string, index: number): string {
      return prefix + (index % 4095).toString(16).padStart(3, "0").toUpperCase();
   }

   /**
    * @function 在table_info中注册表信息
    * @param tableName  表名
    * @param total      当前表数据总数
    */
   static async #registerTable(tableName: ModelName | string, total?: number) {
      if (typeof total != "number") {
         const res = await prisma.$queryRawUnsafe<CreateOption.Count[]>(`SELECT count(*) FROM ${tableName}`);
         total = Number(res[0]["count(*)"]);
      }

      let tableInfo = await prisma.table_info.findFirst({ where: { table_name: "table_info" } });

      // 在 table_info 表中不存在其本身的信息
      // 或id前缀长度为30时
      // 重新在 table_info 中注册其信息
      if (!tableInfo) {
         const idPrefix = this.#nanoid();

         tableInfo = await prisma.table_info.create({
            data: {
               table_name: "table_info",
               id: idPrefix + "000",
               id_prefix: idPrefix,
               next_index: 1,
               total: 1
            }
         });
      }

      if (tableInfo.id_prefix.length == 30) {
         await prisma.table_info.delete({
            where: { id: tableInfo.id }
         });

         await this.#registerTable(tableName, total);
      }

      let idPrefix = this.#nanoid();

      // 在 table_info 表中创建信息的记录
      const newTable = await prisma.table_info.create({
         data: {
            id: this.#getID(tableInfo.id_prefix, tableInfo.next_index),
            id_prefix: idPrefix,
            table_name: tableName,
            next_index: 0,
            total: total
         }
      });

      // 更新 table_info 表中关于 table_info 的记录
      await prisma.table_info.update({
         where: { id: tableInfo.id },
         data: { next_index: tableInfo.next_index + 1, total: tableInfo.total + 1 }
      });

      return newTable;
   }

   /**
    * @function 将已耗尽前缀存入历史记录
    * @param tableId 表 id
    * @param prefixs 历史前缀
    */
   static async #DepositHistory(tableId: string, prefixs: string[]) {
      const history = [...prefixs];
      await prisma.table_info.update({
         where: { id: tableId },
         data: {
            history_prefix: history.join(",")
         }
      });
   }

   /**
    * 创建单个id
    * @param option 配置
    * @param callback 条目创建函数
    *
    * @example
    * created = await Create.ID({ tableName: "test_table" }, async id => {
    *    await prisma.test_table.create({
    *       data: {
    *          id,
    *          created_time: Time.Now()
    *       }
    *    });
    * });
    */
   static async ID(option: CreateOption.ID, callback: CreateOption.IDCallBack): Promise<boolean> {
      const { tableName } = option;

      let count = 0;

      // 获取指定数据库条目数量
      try {
         const rawRes = await prisma.$queryRawUnsafe<CreateOption.Count[]>(`SELECT count(*) FROM ${tableName};`);
         count = Number(rawRes[0]["count(*)"]);
      } catch (error) {
         return false;
      }

      let index = 0; // 初始化序号位 0

      // 查询在 table_info 表中记录的 id 前缀
      let tableInfo = await prisma.table_info.findFirst({ where: { table_name: tableName } });

      let idPrefix = "";

      // 如果查询不到表格信息，则在 table_info 中注册
      if (tableInfo == null) tableInfo = await this.#registerTable(tableName, count);

      index = tableInfo.next_index;
      idPrefix = tableInfo.id_prefix;

      // 检测前缀长度是否为 30
      // 如果是，则在 table_info 中重新注册
      if (idPrefix.length == 30) {
         await prisma.table_info.delete({ where: { id: tableInfo.id } });

         tableInfo = await this.#registerTable(tableName, count);
      }

      index = tableInfo.next_index;
      idPrefix = tableInfo.id_prefix;
      let history = tableInfo.history_prefix?.split(",") || [];

      // 当序号为 4095 时，生成新的 id 前缀
      if (index == 4095) {
         history.unshift(idPrefix);
         idPrefix = this.#nanoid();
      }

      const id = this.#getID(idPrefix, index);

      try {
         await callback(id);

         await prisma.table_info.update({
            where: { id: tableInfo.id },
            data: { id_prefix: idPrefix, next_index: index + 1, total: count + 1 }
         });

         if (history.length > 0) await this.#DepositHistory(tableInfo.id, history);
      } catch (error) {
         return false;
      }

      return true;
   }

   /**
    * 创建多个 id
    * @param option 配置
    * @param callback 条目创建函数
    *
    * @example
    * created = await Create.IDs({ tableName: "test_table", total }, async ids => {
    *    const dataList: Prisma.test_tableCreateManyInput[] = [];
    *
    *    ids.forEach(item => {
    *       dataList.push({
    *          id: item,
    *          created_time: Time.Now()
    *       });
    *    });
    *
    *    await prisma.test_table.createMany({
    *       data: dataList
    *    });
    * });
    */
   static async IDs(option: CreateOption.IDs, callback: CreateOption.IDsCallBack): Promise<boolean> {
      const { tableName, total } = option;

      if (total == 0) return false;

      let count = 0;

      // 获取指定数据库条目数量
      try {
         const rawRes = await prisma.$queryRawUnsafe<CreateOption.Count[]>(`SELECT count(*) FROM ${tableName};`);
         count = Number(rawRes[0]["count(*)"]);
      } catch (error) {
         return false;
      }

      let index = 0; // 初始化序号位 0

      // 查询在 table_info 表中记录的 id 前缀
      let tableInfo = await prisma.table_info.findFirst({ where: { table_name: tableName } });

      let idPrefix = "";

      // 如果查询不到表格信息，则在 table_info 中注册
      if (tableInfo == null) tableInfo = await this.#registerTable(tableName, count);

      index = tableInfo.next_index;
      idPrefix = tableInfo.id_prefix;

      // 检测前缀长度是否为 30
      // 如果是，则在 table_info 中重新注册
      if (idPrefix.length == 30) {
         await prisma.table_info.delete({ where: { id: tableInfo.id } });

         tableInfo = await this.#registerTable(tableName, count);
      }

      index = tableInfo.next_index;
      idPrefix = tableInfo.id_prefix;

      let history = tableInfo.history_prefix?.split(",") || [];
      let ids: string[] = [];

      for (let i = index; i < total + index; i++) {
         // 当序号超过 4095 时，生成新的 id 前缀
         if (i % 4095 === 0) {
            if (i != 0) history.unshift(idPrefix);
            idPrefix = this.#nanoid();
         }

         ids.push(this.#getID(idPrefix, i));
      }

      try {
         await callback(ids);

         await prisma.table_info.update({
            where: { id: tableInfo.id },
            data: { id_prefix: idPrefix, next_index: index + total, total: count + total }
         });

         if (history.length > 0) await this.#DepositHistory(tableInfo.id, history);
      } catch (error) {
         console.log("error", error);
         return false;
      }

      return true;
   }
}
